feat: system widgets foundation + price widget#895
Conversation
# Conflicts: # CHANGELOG.md
| override suspend fun fetchData(): Result<PriceDTO> { | ||
| val period = widgetsStore.data.first().pricePreferences.period ?: GraphPeriod.ONE_DAY | ||
| return fetchData(period) | ||
| } |
There was a problem hiding this comment.
Bug: fetchData() no-arg overload can throw instead of returning Result.failure
The refactored no-arg fetchData() overload no longer wraps widgetsStore.data.first() in runCatching. The original implementation wrapped the entire body, so DataStore IOException or CorruptionException from reading widget preferences were captured as Result.failure. Now they propagate as thrown exceptions to callers who expect only Result success/failure.
| override suspend fun fetchData(): Result<PriceDTO> { | |
| val period = widgetsStore.data.first().pricePreferences.period ?: GraphPeriod.ONE_DAY | |
| return fetchData(period) | |
| } | |
| override suspend fun fetchData(): Result<PriceDTO> = runCatching { | |
| val period = widgetsStore.data.first().pricePreferences.period ?: GraphPeriod.ONE_DAY | |
| fetchData(period).getOrThrow() | |
| } |
| color: ColorProvider? = null, | ||
| maxLines: Int = Int.MAX_VALUE, | ||
| ) { | ||
| Text(text = text, modifier = modifier, style = GlanceTextStyles.subtitle.withColor(color), maxLines = maxLines) |
There was a problem hiding this comment.
CLAUDE.md violation: modifier is not the last argument in composable calls
All 6 Text(...) calls in this file pass modifier as the second argument, with style and maxLines following it. Per CLAUDE.md: "ALWAYS pass modifier = ... as the LAST argument in composable calls"
The same pattern occurs on all 6 wrapper composables (lines 17, 27, 37, 47, 57, 67).
| Text(text = text, modifier = modifier, style = GlanceTextStyles.subtitle.withColor(color), maxLines = maxLines) | |
| Text(text = text, style = GlanceTextStyles.subtitle.withColor(color), maxLines = maxLines, modifier = modifier) |
Apply the same reordering to BodyMSB (line 27), BodySSB (line 37), BodySB (line 47), CaptionB (line 57), and FootnoteM (line 67).
| Row( | ||
| modifier = Modifier | ||
| .padding(vertical = 21.dp, horizontal = 16.dp) | ||
| .fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.spacedBy(16.dp), | ||
| ) { |
There was a problem hiding this comment.
CLAUDE.md violation: modifier is not the last argument in Row composable call
modifier is passed as the first argument with horizontalArrangement following it. Per CLAUDE.md: "ALWAYS pass modifier = ... as the LAST argument in composable calls"
| Row( | |
| modifier = Modifier | |
| .padding(vertical = 21.dp, horizontal = 16.dp) | |
| .fillMaxWidth(), | |
| horizontalArrangement = Arrangement.spacedBy(16.dp), | |
| ) { | |
| Row( | |
| horizontalArrangement = Arrangement.spacedBy(16.dp), | |
| modifier = Modifier | |
| .padding(vertical = 21.dp, horizontal = 16.dp) | |
| .fillMaxWidth() | |
| ) { |
| Column( | ||
| modifier = Modifier | ||
| .padding(horizontal = 16.dp) | ||
| .weight(1f) | ||
| .verticalScroll(rememberScrollState()), | ||
| ) { |
There was a problem hiding this comment.
CLAUDE.md violation: trailing comma after modifier = ... at call sites
Per CLAUDE.md: "NEVER add a trailing comma to modifier = ... at call sites"
This Column call is the first occurrence. The same violation also appears at:
SecondaryButton(line 131)PrimaryButton(line 139)BodySSB(line 162)Icon(line 169)
| Column( | |
| modifier = Modifier | |
| .padding(horizontal = 16.dp) | |
| .weight(1f) | |
| .verticalScroll(rememberScrollState()), | |
| ) { | |
| Column( | |
| modifier = Modifier | |
| .padding(horizontal = 16.dp) | |
| .weight(1f) | |
| .verticalScroll(rememberScrollState()) | |
| ) { |
This PR:
Description
Introduces the foundation for Android home screen widgets (AppWidgets) using Jetpack Glance, starting with the Price widget. The widget displays enabled trading pairs with price, change percentage, and a line chart — matching the in-app Price widget layout.
The architecture reuses the existing
PriceServicefor data fetching (via a newfetchData(period)overload) while keeping widget preferences completely independent from the in-app widget system through a dedicatedAppWidgetPreferencesStorebacked by its own DataStore file.A Glance design system (
GlanceTextStyles,GlanceColors) ports the app's typography and color tokens to Glance equivalents, and shared components (GlanceWidgetScaffold,GlanceDataRow) provide consistent widget layouts.Preview
Screen_recording_20260421_153032.webm
QA Notes